using System;
using System.Collections.Generic;
using System.Text;

using Microsoft.Xna.Framework;

namespace ZombieHigh2.BodySystem
{
    public class BodyManager
    {
        private List<Body> _bodyList;

        public BodyManager()
        {
            _bodyList = new List<Body>();
        }

        public void AddBody(Body body)
        {
            _bodyList.Add(body);
        }

        public void RemoveBody(Body body)
        {
            _bodyList.Remove(body);
        }

        /*
         * Checks the X direction, so along the Z axis at position X
         * 
         *           |
         *          X|
         *           |
         */
        //private Boolean CheckCollisionX(Map map, int x, int z, int depth, out int tilecoordx)
        //{
        //    int tilezpixels = z - (z % map.TileSize);
        //    int testend = z + depth;

        //    tilecoordx = x / map.TileSize;

        //    int tilecoordz = tilezpixels / map.TileSize;

        //    while (tilezpixels <= testend)
        //    {
        //        if (!map.CanMove(tilecoordx, tilecoordz))
        //            return true;

        //        tilecoordz++;
        //        tilezpixels += map.TileSize;
        //    }

        //    return false;
        //}

        //private Boolean CheckCollisionZ(Map map, int x, int z, int width, out int tilecoordz)
        //{
        //    int tilexpixels = x - (x % map.TileSize);
        //    int testend = x + width;

        //    tilecoordz = z / map.TileSize;

        //    int tilecoordx = tilexpixels / map.TileSize;

        //    while (tilexpixels <= testend)
        //    {
        //        if (!map.CanMove(tilecoordx, tilecoordz))
        //            return true;

        //        tilecoordx++;
        //        tilexpixels += map.TileSize;
        //    }

        //    return false;
        //}

        /// <summary>
        /// Only checks against other bodies in the XZ plane
        /// </summary>
        /// <param name="body">Body that is being checked</param>
        /// <param name="velx">Velocity in X direction</param>
        /// <param name="velz">Velocity in Z direction</param>
        /// <returns></returns>
        Boolean CheckAgainstOtherBodies(Body body, 
            float velx, 
            float vely, 
            float velz, 
            out float newx, 
            out float newy, 
            out float newz)
        {

            newx = 0;
            newy = 0;
            newz = 0;

            /* added Y component */
            BoundingBox bbox = new BoundingBox(
                new Vector3(body.AABB.Min.X + velx,
                body.AABB.Min.Y + vely,
                body.AABB.Min.Z + velz),
                new Vector3(body.AABB.Max.X + velx,
                body.AABB.Max.Y + vely,
                body.AABB.Max.Z + velz));

            BoundingBox obbox = new BoundingBox(new Vector3(0,0,0), new Vector3(0,0,0));

            foreach (Body otherBody in _bodyList)
            {
                if (otherBody != body)
                {
                    obbox.Min.X = otherBody.AABB.Min.X;
                    obbox.Min.Y = otherBody.AABB.Min.Y; //added for Y component
                    obbox.Min.Z = otherBody.AABB.Min.Z;
                    obbox.Max.X = otherBody.AABB.Max.X;
                    obbox.Max.Y = otherBody.AABB.Max.Y; //y component
                    obbox.Max.Z = otherBody.AABB.Max.Z;

                    if (bbox.Intersects(obbox))
                    {
                        if (otherBody.AffectBodies && body.AffectBodies)
                        {
                            if (velx > 0)
                            {
                                newx = obbox.Min.X - body.Width - 1;
                            }
                            else if (velx < 0)
                            {
                                newx = obbox.Max.X + 1;
                            }

                            if (vely > 0)
                            {
                                newy = obbox.Min.Y - body.Height - 1;
                            }
                            else if (vely < 0)
                            {
                                newy = obbox.Max.Y + 1;
                            }

                            if (velz > 0)
                            {
                                newz = obbox.Min.Z - body.Depth - 1;
                            }
                            else if (velz < 0)
                            {   
                                newz = obbox.Max.Z + 1;
                            }

                            return true;
                        }

                        //call the body contact methods
                        body.OnContact(otherBody);
                        otherBody.OnContact(body);

                        
                    }
                }
            }
            
            return false;
        }

        public void CheckAgainstMap(GameTime gameTime)
        {
            Body bodyA;
            int tilecoord;

            float x;
            float z;
            float y;

            float newx;
            float newy;
            float newz;

            float velx;
            float vely;
            float velz;

            float elapsedTime = (float)gameTime.ElapsedGameTime.TotalSeconds;

            bool hitGround = false;


            //do collision checks on the velocities
            //Map map = (Map)ZombieHigh.Instance.Services.GetService(typeof(Map));

            for (int b = 0; b < _bodyList.Count; b++)
            {
                bodyA = _bodyList[b];

                y = bodyA.Position.Y;
                x = bodyA.Position.X;
                z = bodyA.Position.Z;

                velx = bodyA.Velocity.X;
                vely = bodyA.Velocity.Y;
                velz = bodyA.Velocity.Z;

                /* 
                 * Check the X velocity
                 * */
                if (bodyA.Velocity.X > 0)   //moving right
                {
                    if (CheckCollisionX(map, (int)(bodyA.Position.X + (bodyA.Velocity.X * elapsedTime) + bodyA.Width), (int)bodyA.Position.Z, (int)bodyA.Depth, out tilecoord))
                        x = tilecoord * map.TileSize - (int)bodyA.Width - 1;
                    else
                    {
                        //No collision with map so check against other bodies
                        if (CheckAgainstOtherBodies(bodyA, bodyA.Velocity.X * elapsedTime,0, 0, out newx, out newy, out newz))
                            x = newx;
                        else
                            x += (bodyA.Velocity.X * elapsedTime);
                    }
                }
                else if (bodyA.Velocity.X < 0)  //moving left
                {
                    if (CheckCollisionX(map, (int)(bodyA.Position.X + (bodyA.Velocity.X * elapsedTime)), (int)bodyA.Position.Z, (int)bodyA.Depth, out tilecoord))
                        x = (tilecoord + 1) * map.TileSize + 1;
                    else
                    {
                        //No collision with map, check against other bodies
                        if (CheckAgainstOtherBodies(bodyA, (bodyA.Velocity.X * elapsedTime), 0, 0,out newx, out newy, out newz))
                            x = newx;
                        else
                            x += (bodyA.Velocity.X * elapsedTime);
                    }
                }

                /* Gravity component */
                #region Gravity Collisions
                if (bodyA.Gravity)
                {
                    
                    if (bodyA.Position.Y - bodyA.Height - (bodyA.Velocity.Y * elapsedTime) < 0)
                    {
                        y = bodyA.Height;
                        //bodyA.Velocity.Y = 0f;
                        vely = 0f;

                        hitGround = true;

                        if (bodyA.Bounce)
                        {
                            vely = -bodyA.Velocity.Y * bodyA.Coefficient;
                        }
                    }
                    else
                    {
                        y -= bodyA.Velocity.Y * elapsedTime;
                        //bodyA.Velocity.Y -= 10f;
                        vely += 10f;
                    }

                }
                else
                {
                    if (bodyA.Position.Y - bodyA.Height < 0)
                    {
                        y = bodyA.Height;
                    }
                }
                #endregion

                /*
                 * Check the Z velocity
                 * */
                if (bodyA.Velocity.Z > 0) //moving into screen
                {
                    if (CheckCollisionZ(map, (int)bodyA.Position.X, (int)(bodyA.Position.Z + (bodyA.Velocity.Z * elapsedTime) + bodyA.Depth), (int)bodyA.Width, out tilecoord))
                        z = tilecoord * map.TileSize - (int)bodyA.Depth - 1;
                    else
                    {
                        if (CheckAgainstOtherBodies(bodyA, 0, 0, bodyA.Velocity.Z * elapsedTime, out newx, out newy, out newz))
                            z = newz;
                        else
                            z += (bodyA.Velocity.Z * elapsedTime);
                    }
                }
                else if (bodyA.Velocity.Z < 0) // moving out of screen
                {
                    if (CheckCollisionZ(map, (int)bodyA.Position.X, (int)(bodyA.Position.Z + (bodyA.Velocity.Z * elapsedTime)), (int)bodyA.Width, out tilecoord))
                        z = (tilecoord + 1) * map.TileSize + 1;
                    else
                    {
                        if (CheckAgainstOtherBodies(bodyA, 0, 0, bodyA.Velocity.Z * elapsedTime, out newx, out newy, out newz))
                            z = newz;
                        else
                            z += (bodyA.Velocity.Z * elapsedTime);
                    }
                }

                bodyA.X = x;
                bodyA.Y = y;
                bodyA.Z = z;

                bodyA.Velocity = new Vector3(velx, vely, velz);

                if (hitGround)
                {
                    hitGround = false;
                    bodyA.OnGroundHit();
                }
            }
        }

        public void Update()
        {
            List<Body> removeList = new List<Body>();

            foreach (Body body in _bodyList)
            {
                if (body.Remove)
                    removeList.Add(body);
            }

            foreach (Body removal in removeList)
            {
                _bodyList.Remove(removal);
            }
        }
    }
}
